import electronLog from "electron-log"
import fs from "fs-extra"
import { createServer as createHttpServer } from "http"
import { createServer as createHttpsServer } from "https"
import lodash from "lodash"
import os from "os"
import path from "path"
import selfsigned from "selfsigned"
import socketio from "socket.io"
import { Socket } from "socket.io-client"

const MAX_CONNECTIONS = 20
const { isFunction, isUndefined } = lodash
const CERT_FOLDER = "./certs"
const CHUNK_SIZE = 64 * 1024 // 64KB chunks

interface ServerOptions {
    httpPort?: number
    httpsPort?: number
    generateNewCert?: boolean
}

interface RegisterProps {
    ip: string
    port: number
    name: string
}

interface RegisterResponse {
    status: "success" | "failed"
    message?: string
    hostname?: string
}

class ConnectManager {
    io: socketio.Server
    connected: boolean
    mobiles: Map<string, RegisterProps>
    httpPort: number
    httpsPort: number

    /**
     * 创建服务器管理器
     * @param options 服务器选项
     * @param options.httpPort HTTP端口，默认3000
     * @param options.httpsPort HTTPS端口，默认3001
     * @param options.generateNewCert 是否生成新证书，默认false
     */
    constructor(options: ServerOptions = {}) {
        const {
            httpPort = 3002,
            httpsPort = 3003,
            generateNewCert = false,
        } = options

        electronLog.info("Constructor called:", {
            httpPort,
            httpsPort,
            generateNewCert,
            timestamp: new Date().toISOString(),
        })

        this.connected = false
        this.mobiles = new Map<string, RegisterProps>()
        this.httpPort = httpPort
        this.httpsPort = httpsPort

        // 确保证书目录存在
        this.ensureCertFolder()

        // 获取或生成证书
        const { key, cert } = this.getCertificates(generateNewCert)

        // 创建HTTP服务器
        const httpServer = createHttpServer()

        // 创建HTTPS服务器
        const httpsServer = createHttpsServer({ key, cert })

        // 创建Socket.IO实例，设置共享配置
        this.io = socketio({
            cors: {
                origin: "*",
                methods: ["GET", "POST"],
            },
            maxHttpBufferSize: 1e8, // 100MB
            pingTimeout: 60000,
            pingInterval: 25000,
            transports: ["websocket", "polling"],
        })

        // 将Socket.IO连接到两个服务器
        this.io.attach(httpServer)
        this.io.attach(httpsServer)

        // 启动HTTP服务器
        httpServer.listen(httpPort, () => {
            electronLog.info(`HTTP Server started on port ${httpPort}`)
        })

        // 启动HTTPS服务器
        httpsServer.listen(httpsPort, () => {
            electronLog.info(`HTTPS Server started on port ${httpsPort}`)
        })

        // 添加错误处理
        httpServer.on("error", (err) => {
            electronLog.error(`HTTP Server error:`, err)
        })

        httpsServer.on("error", (err) => {
            electronLog.error(`HTTPS Server error:`, err)
        })

        this.handleOfServer()
    }

    /**
     * 确保证书文件夹存在
     */
    private ensureCertFolder() {
        try {
            fs.ensureDirSync(CERT_FOLDER)
            electronLog.info(`Certificate folder exists: ${CERT_FOLDER}`)
        } catch (err) {
            electronLog.error(`Failed to create certificate folder:`, err)
            throw err
        }
    }

    /**
     * 获取或生成证书
     * @param generateNew 是否强制生成新证书
     * @returns 证书和密钥
     */
    private getCertificates(generateNew: boolean = false): {
        key: string
        cert: string
    } {
        const keyPath = path.join(CERT_FOLDER, "key.pem")
        const certPath = path.join(CERT_FOLDER, "cert.pem")

        // 如果证书已存在且不需要重新生成
        if (!generateNew && fs.existsSync(keyPath) && fs.existsSync(certPath)) {
            electronLog.info("Using existing certificates")
            return {
                key: fs.readFileSync(keyPath, "utf8"),
                cert: fs.readFileSync(certPath, "utf8"),
            }
        }

        // 生成新证书
        electronLog.info("Generating new self-signed certificate")
        const attrs = [{ name: "commonName", value: "localhost" }]
        const pems = selfsigned.generate(attrs, {
            days: 365,
            algorithm: "sha256",
            keySize: 2048,
            extensions: [
                {
                    name: "subjectAltName",
                    altNames: [
                        { type: 2, value: "localhost" },
                        { type: 2, value: os.hostname() },
                        { type: 7, ip: "127.0.0.1" },
                    ],
                },
            ],
        })

        // 保存证书
        fs.writeFileSync(keyPath, pems.private)
        fs.writeFileSync(certPath, pems.cert)

        electronLog.info("New certificates generated and saved")
        return {
            key: pems.private,
            cert: pems.cert,
        }
    }

    calculateChecksum(data: Buffer | Uint8Array): string {
        // 简单实现：计算前100字节的和
        let sum = 0
        const len = Math.min(data.length, 100)
        for (let i = 0; i < len; i++) {
            sum += data[i]
        }
        return sum.toString(16)
    }

    handleOfServer() {
        electronLog.info("handleOfServer called:", {
            timestamp: new Date().toISOString(),
        })
        electronLog.info("Socket.IO server initialized") // 初始化

        this.io.on("connection", (socket: Socket) => {
            electronLog.info("Client connection details:", {
                id: socket.id,
                address: socket.handshake.address,
                transport: socket.conn.transport.name,
                query: socket.handshake.query,
                headers: socket.handshake.headers,
                time: new Date().toISOString(),
                clientsCount: this.io.engine.clientsCount,
            })

            socket.on("register", (data, callback) => {
                const connectedCount = Object.keys(
                    this.io.sockets.connected
                ).length

                electronLog.info("Register request received:", {
                    clientId: socket.id,
                    clientCount: this.io.engine.clientsCount,
                    activeConnections: connectedCount,
                    socketSize: this.io.sockets.sockets.size,
                    data,
                })

                if (isFunction(callback)) {
                    const response: RegisterResponse = {
                        status:
                            connectedCount <= MAX_CONNECTIONS
                                ? "success"
                                : "failed",
                        hostname:
                            connectedCount <= MAX_CONNECTIONS
                                ? os.hostname()
                                : undefined,
                        message:
                            connectedCount <= MAX_CONNECTIONS
                                ? undefined
                                : "reach max connection limits",
                    }
                    callback(JSON.stringify(response))

                    if (response.status === "success") {
                        this.onRegister(socket.id, data)

                        // 如果注册成功,5秒后发送文件
                        electronLog.info(
                            "Scheduling file transmission in 5 seconds"
                        )
                        setTimeout(() => {
                            electronLog.info(
                                "Starting scheduled file transmission"
                            )
                            this.transmitFile1(socket.id, "./123.png")
                                .then((result) => {
                                    electronLog.info(
                                        "Scheduled file transmission completed:",
                                        result
                                    )
                                })
                                .catch((err) => {
                                    electronLog.error(
                                        "Scheduled file transmission failed:",
                                        err
                                    )
                                })
                        }, 5000)
                    }
                }
            })
            // 处理客户端返回的文件数据
            socket.on("file-data", (buffer, callback) => {
                try {
                    // 记录接收到的数据信息
                    electronLog.info("Received file data from client:", {
                        clientId: socket.id,
                        dataSize: buffer.length,
                        dataType: typeof buffer,
                        // 显示前32字节的十六进制表示(如果可用)
                        preview:
                            buffer instanceof Uint8Array ||
                            Buffer.isBuffer(buffer)
                                ? Buffer.from(buffer)
                                      .slice(0, 32)
                                      .toString("hex")
                                : "Not a binary buffer",
                    })

                    // 验证数据
                    if (
                        !(buffer instanceof Uint8Array) &&
                        !Buffer.isBuffer(buffer)
                    ) {
                        electronLog.warn("Received non-binary data:", {
                            clientId: socket.id,
                            dataType: typeof buffer,
                        })
                    }

                    // 处理数据(例如保存到文件或进一步处理)
                    this.processReceivedfileData(socket.id, buffer)

                    // 发送确认回复
                    if (typeof callback === "function") {
                        callback({
                            status: "success",
                            timestamp: new Date().toISOString(),
                            receivedSize: buffer.length,
                        })
                    }
                } catch (err) {
                    electronLog.error(
                        "Error handling file data from client:",
                        {
                            clientId: socket.id,
                            error: err,
                        }
                    )

                    // 如果处理出错，发送错误响应
                    if (typeof callback === "function") {
                        callback({
                            status: "error",
                            message: err.message,
                            timestamp: new Date().toISOString(),
                        })
                    }
                }
            })

            socket.on("disconnect", (reason) => {
                electronLog.info("Client disconnected:", {
                    clientId: socket.id,
                    reason,
                    remainingClients: this.io.engine.clientsCount,
                })
                this.onDisconnect(socket.id)
            })

            // 添加错误处理
            socket.on("error", (err) => {
                electronLog.error("Socket error:", {
                    socketId: socket.id,
                    error: err,
                })
            })
        })

        this.io.engine.on(
            "connection_error",
            (err: { req: any; code: any; message: any; context: any }) => {
                electronLog.error("Connection error:", {
                    code: err.code,
                    message: err.message,
                    context: err.context,
                })
            }
        )
    }

    deconstruct() {
        electronLog.info("Server shutting down...")

        Object.keys(this.io.sockets.connected).forEach((socketId) => {
            this.io.sockets.connected[socketId].disconnect(true)
        })

        this.io.removeAllListeners()
        this.io.close(() => {
            electronLog.info("Server closed successfully")
        })
    }

    onRegister(id: string, data: string) {
        this.connected = true
        try {
            const json = JSON.parse(data)
            this.mobiles.set(id, json)
            electronLog.info("Client registered:", {
                id,
                data: json,
            })
        } catch (err) {
            electronLog.error("Error parsing register data:", err)
        }
    }

    onDisconnect(id: string) {
        electronLog.info("onDisconnect:", {
            id,
            clientCount: this.io.engine.clientsCount,
            socketSize: this.io.sockets.sockets.size,
        })

        this.mobiles.delete(id)
        if (this.io.sockets.sockets.size === 0 && this.mobiles.size === 0) {
            this.connected = false
            this.mobiles.clear()
        }
    }

    transmitInfo(id: string) {
        const socket = this.io.sockets.connected[id]
        if (isUndefined(socket)) return Promise.resolve(false)

        const data = {
            id: 1,
            data: "qwe",
        }

        return new Promise((resolve, reject) => {
            socket.emit("transmit-info", data, (err: any) => {
                if (err) {
                    electronLog.error("transmit-info error:", err.message)
                    return reject(err)
                }
                electronLog.info("transmit-info reply")
                return resolve(true)
            })
        })
    }

    transmitFile(id: string, file: string) {
        try {
            if (!fs.existsSync(file)) {
                electronLog.error("file file not found:", file)
                return Promise.resolve(false)
            }

            const data = fs.readFileSync(file)
            const socket = this.io.sockets.connected[id]
            if (isUndefined(socket)) return Promise.resolve(false)

            const chunks = Math.ceil(data.length / CHUNK_SIZE)

            return new Promise((resolve, reject) => {
                // 首先发送元数据
                const metadata = {
                    total: chunks,
                    size: data.length,
                }

                socket.emit("transmit-file-start", JSON.stringify(metadata))

                // 延迟发送数据块，给客户端一些处理时间
                setTimeout(() => {
                    try {
                        // 逐个发送数据块
                        for (let i = 0; i < chunks; i++) {
                            const start = i * CHUNK_SIZE
                            const end = Math.min(
                                (i + 1) * CHUNK_SIZE,
                                data.length
                            )

                            // 使用 Buffer.from 创建副本，避免数据共享问题
                            const chunkBuffer = Buffer.from(
                                data.slice(start, end)
                            )
                            const chunk = new Uint8Array(chunkBuffer)

                            electronLog.info(
                                `Sending chunk ${i + 1}/${chunks}, size: ${
                                    chunk.length
                                }`
                            )

                            // 使用二进制事件发送
                            socket.emit("transmit-file-chunk", chunk)

                            // 添加小延迟，避免发送太快
                            if (i < chunks - 1) {
                                const delay = 100 // 100ms delay between chunks
                                Atomics.wait(
                                    new Int32Array(new SharedArrayBuffer(4)),
                                    0,
                                    0,
                                    delay
                                )
                            }
                        }

                        // 发送结束信号
                        socket.emit("transmit-file-end", "")
                        resolve(true)
                    } catch (err) {
                        electronLog.error(
                            "Error during chunk transmission:",
                            err
                        )
                        reject(err)
                    }
                }, 100) // 给客户端 100ms 准备时间
            })
        } catch (err) {
            electronLog.error("transmitFile error:", err)
            return Promise.resolve(false)
        }
    }

    transmitFile1(id: string, file: string) {
        electronLog.info("Starting file transmission:", {
            clientId: id,
            filePath: file,
        })

        return new Promise((resolve, reject) => {
            // 检查文件
            if (!fs.existsSync(file)) {
                electronLog.error("file file not found:", file)
                return resolve(false)
            }

            // 获取socket
            const socket = this.io.sockets.connected[id]
            if (isUndefined(socket)) return resolve(false)

            // 异步读取文件
            fs.readFile(file, (err, data) => {
                if (err) {
                    electronLog.error("Error reading file file:", err)
                    return resolve(false)
                }

                try {
                    // 直接使用二进制数据
                    electronLog.info("Preparing binary data:", {
                        size: data.length,
                        type: "Buffer",
                        preview: data.slice(0, 16).toString("hex"),
                    })

                    // 使用二进制事件发送
                    socket.emit("transmit-file", data, { binary: true })

                    electronLog.info("Binary data sent successfully:", {
                        size: data.length,
                    })
                    resolve(true)
                } catch (err) {
                    electronLog.error("Error sending binary data:", err)
                    resolve(false)
                }
            })
        })
    }
    /**
     * 处理从客户端接收到的文件数据
     * @param clientId 客户端ID
     * @param data 文件数据
     */
    processReceivedfileData1(clientId: string, data: Uint8Array | Buffer) {
        // 这个函数用于简单的打印回执

        electronLog.info("Processing file data:", {
            clientId,
            dataSize: data.length,
            // 计算数据校验和(简单示例)
            checksum: this.calculateChecksum(data),
        })
    }

    /**
     * 根据文件头识别文件类型
     * @param data 二进制数据
     * @returns 文件扩展名
     */
    private detectFileType(data: Buffer | Uint8Array): string {
        // 转换为Buffer以便使用Buffer方法
        const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data)

        // PNG文件（通常以'89 50 4E 47'标记开始）
        if (
            buffer.length >= 8 &&
            buffer.slice(0, 8).toString("hex") === "89504e470d0a1a0a"
        ) {
            return "png"
        }
        // JPG文件（通常以'FF D8 FF'标记开始）
        if (
            buffer.length >= 3 &&
            buffer.slice(0, 3).toString("hex") === "ffd8ff"
        ) {
            return "jpg"
        }
        // GIF文件（通常以'GIF87a'或'GIF89a'标记开始）
        if (
            buffer.length >= 4 &&
            buffer.slice(0, 4).toString("hex") === "47494638"
        ) {
            return "gif"
        }
        // MP4文件（通常以'ftyp'标记开始，位于第4-8字节）
        if (buffer.length >= 12 && buffer.slice(4, 8).toString() === "ftyp") {
            return "mp4"
        }
        // txt文件（通常以'RIFF'标记开始）
        if (
            buffer.length >= 4 &&
            buffer.slice(0, 4).toString("ascii") === "RIFF"
        ) {
            return "txt"
        }
        // 默认返回二进制扩展名
        return "bin"
    }

    /**
     * 处理从客户端接收到的文件数据
     * @param clientId 客户端ID
     * @param data 文件数据
     */
    processReceivedfileData(clientId: string, data: Uint8Array | Buffer) {
        try {
            // 计算文件校验和
            const checksum = this.calculateChecksum(data)

            // 创建唯一的文件名（基于时间戳和客户端ID）
            const timestamp = Date.now()
            const fileExt = this.detectFileType(data)
            const filename = `${fileExt}_${timestamp}.${fileExt}`
            const savePath = path.join("./received_files", filename)

            // 确保保存目录存在
            fs.ensureDirSync("./received_files")

            // 将二进制数据保存为文件
            fs.writeFileSync(savePath, data)

            electronLog.info("Received binary data saved as file:", {
                clientId,
                dataSize: data.length,
                checksum,
                savedTo: savePath,
            })

            return {
                success: true,
                path: savePath,
                size: data.length,
                checksum,
            }
        } catch (err) {
            electronLog.error("Error saving received binary data:", err)
            return {
                success: false,
                error: err.message,
            }
        }
    }
    disconnect(id: string) {
        const socket = this.io.sockets.connected[id]
        electronLog.info("disconnect:", {
            clientId: id,
            clientCount: this.io.engine.clientsCount,
            socketSize: this.io.sockets.sockets.size,
        })

        if (!isUndefined(socket)) {
            socket.disconnect()
        }
    }

    disconnectAll() {
        Object.keys(this.io.sockets.connected).forEach((socketId) => {
            this.io.sockets.connected[socketId].disconnect(true)
        })

        electronLog.info("All clients disconnected")
    }
}

// 应用启动代码
electronLog.info("Starting Socket.IO server application...")

// 使用配置对象创建服务器
const manager = new ConnectManager({
    httpPort: 3000,
    httpsPort: 3001,
    generateNewCert: true, // 是否生成新的证书，第一次运行时打开
})

// 处理进程退出
process.on("SIGINT", () => {
    electronLog.info("Received SIGINT. Cleaning up...")
    manager.deconstruct()
    process.exit(0)
})

// 添加未捕获的异常处理
process.on("uncaughtException", (err) => {
    electronLog.error("Uncaught exception:", err)
})

// 添加未处理的Promise拒绝处理
process.on("unhandledRejection", (reason, promise) => {
    electronLog.error("Unhandled rejection:", {
        reason,
        promise,
    })
})

// 添加内存监控日志
setInterval(() => {
    electronLog.info("Memory usage stats:", {
        memory: process.memoryUsage(),
        timestamp: new Date().toISOString(),
    })
}, 300000) // 每5分钟记录一次内存使用情况

export default ConnectManager
